﻿
#if UNITY_EDITOR

namespace Blaze.Security
{
    using System;
    using System.IO;

    /// <summary>
    /// 异或加密流。
    /// </summary>
    internal class InputStream : Stream
    {
        /// <summary>
        /// 当在派生类中重写时，获取指示当前流是否支持读取的值。
        /// </summary>
        /// <returns>
        /// 如果流支持读取，为 true；否则为 false。
        /// </returns>
        public override bool CanRead
        {
            get { return false; }
        }

        /// <summary>
        /// 当在派生类中重写时，获取指示当前流是否支持查找功能的值。
        /// </summary>
        /// <returns>
        /// 如果流支持查找，为 true；否则为 false。
        /// </returns>
        public override bool CanSeek
        {
            get { return false; }
        }

        /// <summary>
        /// 当在派生类中重写时，获取指示当前流是否支持写入功能的值。
        /// </summary>
        /// <returns>
        /// 如果流支持写入，则为 true；否则为 false。
        /// </returns>
        public override bool CanWrite
        {
            get { return true; }
        }

        /// <summary>
        /// 当在派生类中重写时，获取流长度（以字节为单位）。
        /// </summary>
        /// <returns>
        /// 表示流长度（以字节为单位）的长值。
        /// </returns>
        /// <exception cref="T:System.NotSupportedException">从 Stream 派生的类不支持查找。</exception><exception cref="T:System.ObjectDisposedException">在流关闭后调用方法。</exception>
        public override long Length
        {
            get { return mBaseStream.Length; }
        }

        /// <summary>
        /// 当在派生类中重写时，获取或设置当前流中的位置。
        /// </summary>
        /// <returns>
        /// 流中的当前位置。
        /// </returns>
        /// <exception cref="T:System.IO.IOException">发生 I/O 错误。</exception><exception cref="T:System.NotSupportedException">流不支持查找。</exception><exception cref="T:System.ObjectDisposedException">在流关闭后调用方法。</exception>
        public override long Position
        {
            get { return mBaseStream.Position; }
            set { throw new NotImplementedException(); }
        }

        public InputStream(byte[] key, byte offset, Stream baseStream)
        {
            if (baseStream == null)
                throw new ArgumentNullException("baseStream");
            mStartPosition = baseStream.Position;
            mBaseStream = baseStream;
            mKey = key;
            mOffset = offset;
        }

        /// <summary>
        /// 当在派生类中重写时，将清除该流的所有缓冲区，并使得所有缓冲数据被写入到基础设备。
        /// </summary>
        /// <exception cref="T:System.IO.IOException">发生 I/O 错误。</exception>
        public override void Flush()
        {
            mBaseStream.Flush();
        }

        /// <summary>
        /// 当在派生类中重写时，从当前流读取字节序列，并将此流中的位置提升读取的字节数。
        /// </summary>
        /// <returns>
        /// 读入缓冲区中的总字节数。如果很多字节当前不可用，则总字节数可能小于请求的字节数；如果已到达流结尾，则为零 (0)。
        /// </returns>
        /// <param name="buffer">字节数组。此方法返回时，该缓冲区包含指定的字符数组，该数组的 <paramref name="offset"/> 和 (<paramref name="offset"/> + <paramref name="count"/> -1) 之间的值由从当前源中读取的字节替换。</param><param name="offset"><paramref name="buffer"/> 中的从零开始的字节偏移量，从此处开始存储从当前流中读取的数据。</param><param name="count">要从当前流中最多读取的字节数。</param><exception cref="T:System.ArgumentException"><paramref name="offset"/> 与 <paramref name="count"/> 的和大于缓冲区长度。</exception><exception cref="T:System.ArgumentNullException"><paramref name="buffer"/> 为 null。</exception><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="offset"/> 或 <paramref name="count"/> 为负。</exception><exception cref="T:System.IO.IOException">发生 I/O 错误。</exception><exception cref="T:System.NotSupportedException">流不支持读取。</exception><exception cref="T:System.ObjectDisposedException">在流关闭后调用方法。</exception>
        public override int Read(byte[] buffer, int offset, int count)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// 当在派生类中重写时，设置当前流中的位置。
        /// </summary>
        /// <returns>
        /// 当前流中的新位置。
        /// </returns>
        /// <param name="offset">相对于 <paramref name="origin"/> 参数的字节偏移量。</param><param name="origin"><see cref="T:System.IO.SeekOrigin"/> 类型的值，指示用于获取新位置的参考点。</param><exception cref="T:System.IO.IOException">发生 I/O 错误。</exception><exception cref="T:System.NotSupportedException">流不支持查找，例如在流通过管道或控制台输出构造的情况下即为如此。</exception><exception cref="T:System.ObjectDisposedException">在流关闭后调用方法。</exception>
        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 当在派生类中重写时，设置当前流的长度。
        /// </summary>
        /// <param name="value">所需的当前流的长度（以字节表示）。</param><exception cref="T:System.IO.IOException">发生 I/O 错误。</exception><exception cref="T:System.NotSupportedException">流不支持写入和查找，例如在流通过管道或控制台输出构造的情况下即为如此。</exception><exception cref="T:System.ObjectDisposedException">在流关闭后调用方法。</exception>
        public override void SetLength(long value)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 当在派生类中重写时，向当前流中写入字节序列，并将此流中的当前位置提升写入的字节数。
        /// </summary>
        /// <param name="buffer">字节数组。此方法将 <paramref name="count"/> 个字节从 <paramref name="buffer"/> 复制到当前流。</param><param name="offset"><paramref name="buffer"/> 中的从零开始的字节偏移量，从此处开始将字节复制到当前流。</param><param name="count">要写入当前流的字节数。</param><exception cref="T:System.ArgumentException">总和 <paramref name="offset"/> 和 <paramref name="count"/> 大于缓冲区长度。</exception><exception cref="T:System.ArgumentNullException"><paramref name="buffer"/>  是 null。</exception><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="offset"/> 或 <paramref name="count"/> 为负。</exception><exception cref="T:System.IO.IOException">将出现 I/O 错误，如找不到指定的文件。</exception><exception cref="T:System.NotSupportedException">流不支持写入。</exception><exception cref="T:System.ObjectDisposedException"><see cref="M:System.IO.Stream.Write(System.Byte[],System.Int32,System.Int32)"/> 流关闭后调用。</exception>
        public override void Write(byte[] buffer, int offset, int count)
        {
            var pos = mBaseStream.Position - mStartPosition;
            for (var i = 0; i < count; i++)
            {
                var b = buffer[offset + i] ^ mKey[(pos + i)%mKey.Length];
                b = (b + mOffset)%256;
                buffer[offset + i] = (byte) b;
            }
            mBaseStream.Write(buffer, offset, count);
        }

        private readonly Stream mBaseStream;
        private readonly byte[] mKey;
        private readonly byte mOffset;
        private readonly long mStartPosition;
    }
}

#endif